Terraform 実行元の IAM 情報を取得したい時は aws_iam_session_context が使えます
こんにちは!AWS 事業本部コンサルティング部のたかくに(@takakuni_)です。
完全に備忘録です。
Terraform でデプロイ先のアカウント ID を取得する際に data ブロックの aws_caller_identity
を利用することが多いのではないでしょうか。
私は以下のように、いつも使うテンプレートのようなものを用意して、検証などを行っています。
他の tf ファイルからは local.account_id
を参照すればいいため、 data.aws_caller_identity.self.account_id
を都度参照するより文字数が少なく可読負荷が低く済みます。便利ですよね。
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "5.81.0"
}
}
}
provider "aws" {
# Configuration options
region = "ap-northeast-1"
}
data "aws_caller_identity" "self" {}
data "aws_region" "current" {}
locals {
account_id = data.aws_caller_identity.self.account_id
region = data.aws_region.current.name
prefix = "sample"
}
最近、 OpenSearch Serverless や Kubernetes など、 AWS と AWS 以外の Provider を組み合わせる検証機会が増えてきました。
商用環境などの場合、OpenSearch Serverless, Kubernetes の操作は必ずしも AWS と一致しないため、デプロイのタイミングやパイプライン、操作する IAM の権限を分けて変更を行うと思いますが、検証であれば、ササっと同一のタイミングでデプロイしてしたいケースがあるのではないでしょうか。
その場合、OpenSearch Serverless だとデータアクセスポリシー、 EKS だと ConfigMap やアクセスエントリが挙げられます。
では、Terraform で実行元の IAM をどのように認識すれば良いでしょうか。
data.aws_caller_identity
まずは data.aws_caller_identity
の出力値をみてみましょう。
data "aws_caller_identity" "this" {}
output "aws_caller_identity" {
value = data.aws_caller_identity.this
}
実はアカウント ID 以外も情報を取得できます。
IAM ユーザーは利用しているユーザーの ARN、IAM ロールの場合はセッション名が含まれた状態の ARN が出力されていますね。
# IAM ユーザー
aws_caller_identity = {
"account_id" = "123456789012"
"arn" = "arn:aws:iam::123456789012:user/takakuni"
"id" = "123456789012"
"user_id" = "AIDAXA25AMYQIWW25MA33"
}
# IAM ロール
aws_caller_identity = {
"account_id" = "123456789012"
"arn" = "arn:aws:sts::123456789012:assumed-role/takakuni/botocore-session-1735198227"
"id" = "123456789012"
"user_id" = "AROAXXXXXXXXXXXXXXXXX:botocore-session-1735198227"
}
data.aws_caller_identity.self.arn を利用
そのため、以下のように data.aws_caller_identity.self.arn
と入れてあげるのが良さそうに思えます。
########################################################
# Data Access Policy
########################################################
resource "aws_opensearchserverless_access_policy" "this_data" {
name = "${local.prefix}-data"
type = "data"
description = "${local.prefix}-data"
policy = jsonencode([
{
Rules = [
{
ResourceType = "collection",
Resource = [
"collection/${local.prefix}-collection"
],
Permission = [
"aoss:DescribeCollectionItems",
"aoss:CreateCollectionItems",
"aoss:UpdateCollectionItems",
"aoss:DeleteCollectionItems"
]
},
{
ResourceType = "index",
Resource = [
"index/${local.prefix}-collection/*"
],
Permission = [
"aoss:ReadDocument",
"aoss:WriteDocument",
"aoss:DescribeIndex",
"aoss:CreateIndex",
"aoss:UpdateIndex",
"aoss:DeleteIndex"
],
},
],
Principal = [
data.aws_caller_identity.self.arn
]
}
])
}
リソースの作成には成功します。ただ、 IAM ロールのセッション名が変わった場合、どうなるでしょうか?
正解は OpenSearch プロバイダーで定義したリソースへの読み取りアクセスが弾かれてしまいます。
原因は OpenSearch プロバイダーが既存のリソースを変更されたセッション名で参照しようとするため、データアクセスポリシーを更新する前にエラーになります。
# aws_opensearchserverless_access_policy.this_data will be updated in-place
~ resource "aws_opensearchserverless_access_policy" "this_data" {
id = "sample-data"
name = "sample-data"
~ policy = jsonencode(
~ [
~ {
~ Principal = [
~ "arn:aws:sts::123456789012:assumed-role/takakuni/botocore-session-1735197741" -> "arn:aws:sts::123456789012:assumed-role/takakuni/botocore-session-1735200718",
]
# (1 unchanged attribute hidden)
},
]
)
~ policy_version = "MTczNTE5OTg5MjkyMF8y" -> (known after apply)
# (2 unchanged attributes hidden)
}
Plan: 0 to add, 1 to change, 0 to destroy.
╷
│ Error: elastic: Error 403 (Forbidden): User does not have permissions for the requested resource [type=authorization_exception]
│
│ with opensearch_index.this,
│ on opensesarch.tf line 114, in resource "opensearch_index" "this":
│ 114: resource "opensearch_index" "this" {
これだと検証が止まってしまいますね。
ちなみに EKS の場合は assume-role ではなく role 名を指定するように怒られます。
Terraform will perform the following actions:
# aws_eks_access_entry.this will be created
+ resource "aws_eks_access_entry" "this" {
+ access_entry_arn = (known after apply)
+ cluster_name = "sample-eks-cluster"
+ created_at = (known after apply)
+ id = (known after apply)
+ kubernetes_groups = (known after apply)
+ modified_at = (known after apply)
+ principal_arn = "arn:aws:sts::123456789012:assumed-role/takakuni/botocore-session-1735200718"
+ tags_all = (known after apply)
+ type = "STANDARD"
+ user_name = (known after apply)
}
# aws_eks_access_policy_association.this will be created
+ resource "aws_eks_access_policy_association" "this" {
+ associated_at = (known after apply)
+ cluster_name = "sample-eks-cluster"
+ id = (known after apply)
+ modified_at = (known after apply)
+ policy_arn = "arn:aws:eks::aws:cluster-access-policy/AmazonEKSViewPolicy"
+ principal_arn = "arn:aws:iam::123456789012:role/takakuni"
+ access_scope {
+ type = "cluster"
}
}
Plan: 2 to add, 0 to change, 0 to destroy.
Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.
Enter a value: yes
aws_eks_access_policy_association.this: Creating...
aws_eks_access_entry.this: Creating...
aws_eks_access_policy_association.this: Creation complete after 1s [id=sample-eks-cluster#arn:aws:iam::123456789012:role/takakuni#arn:aws:eks::aws:cluster-access-policy/AmazonEKSViewPolicy]
╷
│ Error: creating EKS Access Entry (sample-eks-cluster:arn:aws:sts::123456789012:assumed-role/takakuni/botocore-session-1735200718): operation error EKS: CreateAccessEntry, https response error StatusCode: 400, RequestID: 93436721-5ba7-4d95-9ca3-9fd97f7e2080, InvalidParameterException: The principalArn parameter format is not valid
│
│ with aws_eks_access_entry.this,
│ on eks.tf line 143, in resource "aws_eks_access_entry" "this":
│ 143: resource "aws_eks_access_entry" "this" {
│
╵
You can't use the STS session principal type with access entries because this is a temporary principal for each session and not a permanent identity that can be assigned permissions.
ワイルドカードを使う
少し荒技ですが、 assume-role の指定が許されている OpenSearch の方はワイルドカードを使ってセッション名の変更をカバーできます。
ただ、複数の関数の組み合わせで 1 回で理解は難しいですね。もっとスマートな方法はないのでしょうか。
########################################################
# Data Access Policy
########################################################
resource "aws_opensearchserverless_access_policy" "this_data" {
name = "${local.prefix}-data"
type = "data"
description = "${local.prefix}-data"
policy = jsonencode([
{
Rules = [
{
ResourceType = "collection",
Resource = [
"collection/${local.prefix}-collection"
],
Permission = [
"aoss:DescribeCollectionItems",
"aoss:CreateCollectionItems",
"aoss:UpdateCollectionItems",
"aoss:DeleteCollectionItems"
]
},
{
ResourceType = "index",
Resource = [
"index/${local.prefix}-collection/*"
],
Permission = [
"aoss:ReadDocument",
"aoss:WriteDocument",
"aoss:DescribeIndex",
"aoss:CreateIndex",
"aoss:UpdateIndex",
"aoss:DeleteIndex"
],
},
],
Principal = [
"${join("/", slice(tolist(split("/", data.aws_caller_identity.self.arn)), 0, 2))}/*"
]
}
])
}
aws_iam_session_context
お待たせしました。 aws_iam_session_context
の出番です。
この data ブロックは実行元の IAM のセッション情報を取得するリソースです。
aws_iam_session_context
のすごいところは、実行元の IAM が IAM ユーザーでも IAM ロールでもシームレスに動くところです。
data "aws_caller_identity" "this" {}
data "aws_iam_session_context" "this" {
arn = data.aws_caller_identity.this.arn
}
output "aws_iam_session_context" {
value = data.aws_iam_session_context.this
}
以下が aws_iam_session_context
を使ったセッション情報の取得結果です。
issuer_arn
を指定すれば、発行元の IAM ロール名を取得したい要件が満たせそうです。
# IAM ロールの場合
aws_iam_session_context = {
"arn" = "arn:aws:sts::123456789012:assumed-role/takakuni/botocore-session-1735198227"
"id" = "arn:aws:sts::123456789012:assumed-role/takakuni/botocore-session-1735198227"
"issuer_arn" = "arn:aws:iam::123456789012:role/takakuni"
"issuer_id" = "AROAXXXXXXXXXXXXXXXXX"
"issuer_name" = "takakuni"
"session_name" = "botocore-session-1735198227"
}
# IAM ユーザーの場合
aws_iam_session_context = {
"arn" = "arn:aws:iam::123456789012:user/takakuni"
"id" = "arn:aws:iam::123456789012:user/takakuni"
"issuer_arn" = "arn:aws:iam::123456789012:user/takakuni"
"issuer_id" = ""
"issuer_name" = ""
"session_name" = ""
}
aws_iam_session_context
を使ってみました。非常にスッキリしましたね。
data "aws_caller_identity" "self" {}
data "aws_iam_session_context" "this" {
arn = data.aws_caller_identity.self.arn
}
###################################################
# EKS Cluster Access Entries
###################################################
resource "aws_eks_access_entry" "this" {
cluster_name = aws_eks_cluster.this.name
principal_arn = data.aws_iam_session_context.this.issuer_arn
# principal_arn = data.aws_caller_identity.self.arn
# principal_arn = "${join("/", slice(tolist(split("/", data.aws_caller_identity.self.arn)), 0, 2))}/*"
type = "STANDARD"
}
resource "aws_eks_access_policy_association" "this" {
cluster_name = aws_eks_cluster.this.name
principal_arn = aws_eks_access_entry.this.principal_arn
policy_arn = "arn:aws:eks::aws:cluster-access-policy/AmazonEKSViewPolicy"
access_scope {
type = "cluster"
}
}
########################################################
# Data Access Policy
########################################################
resource "aws_opensearchserverless_access_policy" "this_data" {
name = "${local.prefix}-data"
type = "data"
description = "${local.prefix}-data"
policy = jsonencode([
{
Rules = [
{
ResourceType = "collection",
Resource = [
"collection/${local.prefix}-collection"
],
Permission = [
"aoss:DescribeCollectionItems",
"aoss:CreateCollectionItems",
"aoss:UpdateCollectionItems",
"aoss:DeleteCollectionItems"
]
},
{
ResourceType = "index",
Resource = [
"index/${local.prefix}-collection/*"
],
Permission = [
"aoss:ReadDocument",
"aoss:WriteDocument",
"aoss:DescribeIndex",
"aoss:CreateIndex",
"aoss:UpdateIndex",
"aoss:DeleteIndex"
],
},
],
Principal = [
data.aws_iam_session_context.this.issuer_arn
]
}
])
}
EKS のアクセスエントリも上手く作れていますし、キャッシュ削除後の OpenSearch が上手く読み込めていることがわかります。
takakuni.shinnosuke@HL01556 sagemaker_hyperpod_eks % terraform apply
╷
│ Warning: Provider development overrides are in effect
│
│ The following provider development overrides are set in the CLI configuration:
│ - hashicorp.com/edu/hashicups in /Users/takakuni.shinnosuke/.asdf/installs/golang/1.23.2/packages/bin
│
│ The behavior may therefore not match any released version of the provider and applying changes may cause the state to become incompatible with published releases.
╵
data.aws_caller_identity.self: Reading...
data.aws_region.current: Reading...
data.aws_iam_policy_document.hyperpod_vpc: Reading...
# 省略
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
# aws_eks_access_entry.this will be created
+ resource "aws_eks_access_entry" "this" {
+ access_entry_arn = (known after apply)
+ cluster_name = "sample-eks-cluster"
+ created_at = (known after apply)
+ id = (known after apply)
+ kubernetes_groups = (known after apply)
+ modified_at = (known after apply)
+ principal_arn = "arn:aws:iam::123456789012:role/takakuni"
+ tags_all = (known after apply)
+ type = "STANDARD"
+ user_name = (known after apply)
}
# aws_eks_access_policy_association.this will be created
+ resource "aws_eks_access_policy_association" "this" {
+ associated_at = (known after apply)
+ cluster_name = "sample-eks-cluster"
+ id = (known after apply)
+ modified_at = (known after apply)
+ policy_arn = "arn:aws:eks::aws:cluster-access-policy/AmazonEKSViewPolicy"
+ principal_arn = "arn:aws:iam::123456789012:role/takakuni"
+ access_scope {
+ type = "cluster"
}
}
Plan: 2 to add, 0 to change, 0 to destroy.
Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.
Enter a value: yes
aws_eks_access_entry.this: Creating...
aws_eks_access_entry.this: Creation complete after 1s [id=sample-eks-cluster:arn:aws:iam::123456789012:role/takakuni]
aws_eks_access_policy_association.this: Creating...
aws_eks_access_policy_association.this: Creation complete after 1s [id=sample-eks-cluster#arn:aws:iam::123456789012:role/takakuni#arn:aws:eks::aws:cluster-access-policy/AmazonEKSViewPolicy]
Apply complete! Resources: 2 added, 0 changed, 0 destroyed.
Outputs:
cluster_name = "sample-eks-cluster"
# キャッシュの削除
takakuni.shinnosuke@HL01556 sagemaker_hyperpod_eks % rm -r ~/.aws/cli/cache
takakuni.shinnosuke@HL01556 sagemaker_hyperpod_eks % terraform apply
╷
│ Warning: Provider development overrides are in effect
│
│ The following provider development overrides are set in the CLI configuration:
│ - hashicorp.com/edu/hashicups in /Users/takakuni.shinnosuke/.asdf/installs/golang/1.23.2/packages/bin
│
│ The behavior may therefore not match any released version of the provider and applying changes may cause the state to become incompatible with published releases.
╵
Enter MFA code for arn:aws:iam::123456789012:mfa/takakuni:
data.aws_region.current: Reading...
data.aws_caller_identity.self: Reading...
data.aws_iam_policy_document.hyperpod_vpc: Reading...
aws_opensearchserverless_security_policy.this_network: Refreshing state... [id=sample-ntwrk]
aws_opensearchserverless_security_policy.this_encryption: Refreshing state... [id=sample-enc]
aws_vpc_endpoint.s3: Refreshing state... [id=vpce-01b62e5aaa1164609]
# 省略
# OpenSearch のリソースを読み込めている
opensearch_index.this: Refreshing state... [id=sample-vector-index]
aws_eks_cluster.this: Refreshing state... [id=sample-eks-cluster]
module.vpc.aws_route.private_nat_gateway[0]: Refreshing state... [id=r-rtb-0827b15aecf4e62ee1080289494]
module.vpc.aws_route.private_nat_gateway[1]: Refreshing state... [id=r-rtb-0dcfee08b12998fdd1080289494]
aws_eks_access_entry.this: Refreshing state... [id=sample-eks-cluster:arn:aws:iam::123456789012:role/takakuni]
aws_eks_addon.kube_proxy: Refreshing state... [id=sample-eks-cluster:kube-proxy]
aws_eks_addon.vpc_cni: Refreshing state... [id=sample-eks-cluster:vpc-cni]
aws_eks_addon.eks_pod_identity_agent: Refreshing state... [id=sample-eks-cluster:eks-pod-identity-agent]
aws_eks_access_policy_association.this: Refreshing state... [id=sample-eks-cluster#arn:aws:iam::123456789012:role/takakuni#arn:aws:eks::aws:cluster-access-policy/AmazonEKSViewPolicy]
No changes. Your infrastructure matches the configuration.
Terraform has compared your real infrastructure against your configuration and found no differences, so no changes are needed.
Apply complete! Resources: 0 added, 0 changed, 0 destroyed.
Outputs:
cluster_name = "sample-eks-cluster"
おわりに
以上、「Terraform 実行元の IAM 情報を取得したい時は aws_iam_session_context が使えます」でした。
EKS を久しぶりに触り、気がつけばアクセスエントリという概念が増えていました。
最近、実行元の IAM を知りたい時が Open Search Serverless の検証でよくあり、ワイルドカード戦法を使っていたのですが、今一度どれが正解なのだ?と調べていると辿り着けました。
EKS Module さん、いつもありがとうございます。
このブログがどなたかの参考になれば幸いです。
AWS 事業本部コンサルティング部のたかくに(@takakuni_)でした!